// D3DDeviceDrv.cpp (c) 2011 JV Software
//

#include "D3DDeviceDrvPrivate.h"

D3DDeviceDrv d3dDevice;
ENGINE_API FGraphicsDevice *graphicsDevice = &d3dDevice;
/*
==============
D3DDeviceDrv::CreateDepthStencilView
==============
*/
void D3DDeviceDrv::CreateDepthStencilView( FWindow *window ) {
	HRESULT hr;

	// Create a render target view
    ID3D11Texture2D* pBackBuffer = NULL;
    hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&pBackBuffer );
	if( FAILED( hr ) ) {
		engine->FatalError( TEXT("SwapChain::GetBuffer: Failed\n" ));
	}

    hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView );
    pBackBuffer->Release();
	if( FAILED( hr ) ) {
        engine->FatalError( TEXT("CreateRenderTargetView: Failed\n" ));
	}

	// Allocate the object
	depthStencilTex2D = AllocObject( D3DTexture2D );
	depthStencilTex2D->InitAsDepthStencilTexture(window->GetWidth(), window->GetHeight());

    // Create the depth stencil view
    D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
    ZeroMemory( &descDSV, sizeof(descDSV) );
    descDSV.Format = depthStencilTex2D->GetDesc().Format;
    descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
    descDSV.Texture2D.MipSlice = 0;
    hr = g_pd3dDevice->CreateDepthStencilView( (ID3D11Resource *)depthStencilTex2D->InternalHandle(), &descDSV, &g_pDepthStencilView );
	if( FAILED( hr ) ) {
        engine->FatalError( TEXT("Create depth stencil view failed...\n" ));
	}

    g_pImmediateContext->OMSetRenderTargets( 1, &g_pRenderTargetView, g_pDepthStencilView );
}

/*
==============
D3DDeviceDrv::LoadEffect
==============
*/
FEffect	*D3DDeviceDrv::LoadEffect( FString path ) {
	D3DDeviceDrvEffect *effect = AllocObject( D3DDeviceDrvEffect );
	FFile *hlslFile;
	
	// Open the file for reading.
	hlslFile = fileSystem->OpenFile( path );
	if(!hlslFile) {
		engine->FatalError( TEXT( "LoadEffect: effect not found\n" ) );
		return NULL;
	}

	// Load in the effect.
	effect->LoadEffect( (byte *)hlslFile->Read( hlslFile->Len() ), hlslFile->Len() );

	FreeObject( hlslFile );

	return effect;
}

/*
==============
D3DDeviceDrv::LoadImage
==============
*/
FTexture *D3DDeviceDrv::LoadImage( FString path ) {
	FTexture *texture = NULL;
	FFile *imgfile = NULL;
	FTextureHeader_t *header;
	
	// Try to open the image file.
	imgfile = fileSystem->OpenFile( path );
	if(!imgfile) {
		FreeObject( imgfile );
		return NULL;
	}

	header = (FTextureHeader_t *)imgfile->Read( imgfile->Len() );

	if(header->version != FTEXTURE_VERSION) {
		engine->FatalError( TEXT("FTexture version invalid") );
		return NULL;
	}

	if(header->format <= TEX_FORMAT_DXT5) {
		texture = AllocObject(D3DTexture2D);
		((D3DTexture2D *)texture)->SetName( path );
		((D3DTexture2D *)texture)->InitFromBuffer( header );
	}
	else {
		engine->FatalError( TEXT("InitImageFromMemory: Invalid image format") );
	}

	FreeObject( imgfile );

	return texture;
}

/*
==============
D3DDeviceDrv::Init
==============
*/
void D3DDeviceDrv::Init( FWindow *window ) {
	UINT createDeviceFlags = 0;
	HRESULT hr;

#ifdef _DEBUG
 //   createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
    };
	UINT numFeatureLevels = ARRAYSIZE( featureLevels );

	engine->Printf( TEXT("D3DDeviceDrv::Init: Init D3D11Device...\n" ) );
	
    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory( &sd, sizeof( sd ) );
    sd.BufferCount = 1;
    sd.BufferDesc.Width = window->GetWidth();
    sd.BufferDesc.Height = window->GetHeight();
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = (HWND)window->GetHandle();
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed = TRUE;

	// Create our swap chain.
    hr = D3D11CreateDeviceAndSwapChain( NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, featureLevels, numFeatureLevels,
                                            D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );
	if( FAILED( hr ) ) {
		engine->FatalError( TEXT("D3D11CreateDeviceAndSwapChain: Failed...\n") );
	}

	if( g_featureLevel == D3D_FEATURE_LEVEL_11_0) {
		engine->Printf(TEXT("D3D11 FeatureSet enabled...\n") );
	}
	else if( g_featureLevel == D3D_FEATURE_LEVEL_10_1) {
		engine->Printf(TEXT("D3D10.1 FeatureSet enabled...\n") );
	}
	else if( g_featureLevel == D3D_FEATURE_LEVEL_10_0) {
		engine->Printf(TEXT("D3D10.0 FeatureSet enabled...\n") );
	}

	// Create the default RTT texture.
	CreateDepthStencilView( window );

	// Set the default attributes for the viewport.
	SetViewport( window->GetWidth(), window->GetHeight(), 0.0f, 1.0f, 0.0f, 0.0f );

	FFile *testFile = fileSystem->OpenFile( TEXT("ui/loadingsplash.ftex"));
	FTextureHeader_t *header = (FTextureHeader_t *)testFile->Read(  testFile->Len() );

	header = header;
}

/*
==============
D3DDeviceDrv::Clear
==============
*/
void D3DDeviceDrv::Clear( void ) {
	// Clear the back buffer 
    float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; // red,green,blue,alpha
    g_pImmediateContext->ClearRenderTargetView( g_pRenderTargetView, ClearColor );
}

/*
==============
D3DDeviceDrv::Present
==============
*/
void D3DDeviceDrv::Present( void ) {
	// Present the information rendered to the back buffer to the front buffer (the screen)
    g_pSwapChain->Present( 0, 0 );
}

/*
==============
D3DDeviceDrv::SetViewport
==============
*/
void D3DDeviceDrv::SetViewport( int width, int height, float mindepth, float maxdepth, float topLeftX, float topLeftY ) {
	// Setup the viewport
    D3D11_VIEWPORT vp;
    vp.Width = (FLOAT)width;
    vp.Height = (FLOAT)height;
    vp.MinDepth = mindepth;
    vp.MaxDepth = maxdepth;
    vp.TopLeftX = topLeftX;
    vp.TopLeftY = topLeftY ;
    g_pImmediateContext->RSSetViewports( 1, &vp );
}